package yang.simpleMain.MyLock;
import java.util.HashMap;
import java.util.Map;

public class ReadWriteLock {
	/** 读锁线程， 进入次数 */
	private Map<Thread, Integer> readingThreads = new HashMap<Thread, Integer>();
	/** 写线程重入次数 */
	private int writeAccesses = 0;
	/** 请求写的线程数 */
	private int writeRequests = 0;
	/** 当前正在写的线程 */
	private Thread writingThread = null;

	public synchronized void lockRead() throws InterruptedException {
		Thread callingThread = Thread.currentThread();
		while (!canGrantReadAccess(callingThread)) {
			wait();
		}
		readingThreads.put(callingThread, (getReadAccessCount(callingThread) + 1));
	}

	private boolean canGrantReadAccess(Thread callingThread) {
		if (isWriter(callingThread))
			// 写锁可读
			return true;

		if (hasWriter())
			// 有写锁则不可进入
			return false;

		if (isReader(callingThread))
			// 写锁可重入
			return true;

		if (hasWriteRequests())
			// 有写锁的请求在排队
			return false;

		return true;
	}

	public synchronized void unlockRead() {
		Thread callingThread = Thread.currentThread();

		if (!isReader(callingThread)) {
			throw new IllegalMonitorStateException("Calling Thread does not hold a read lock on this ReadWriteLock");
		}

		int accessCount = getReadAccessCount(callingThread);

		if (accessCount == 1) {
			readingThreads.remove(callingThread);
		} else {
			readingThreads.put(callingThread, (accessCount - 1));
		}

		notifyAll();
	}

	public synchronized void lockWrite() throws InterruptedException {
		writeRequests++;

		Thread callingThread = Thread.currentThread();

		while (!canGrantWriteAccess(callingThread)) {
			wait();
		}

		writeRequests--;

		writeAccesses++;

		writingThread = callingThread;
	}

	public synchronized void unlockWrite() throws InterruptedException {
		if (!isWriter(Thread.currentThread())) {
			throw new IllegalMonitorStateException("Calling Thread does not hold the write lock on this ReadWriteLock");
		}

		writeAccesses--;

		if (writeAccesses == 0) {
			writingThread = null;
		}

		notifyAll();
	}

	private boolean canGrantWriteAccess(Thread callingThread) {
		if (isOnlyReader(callingThread))
			// 有且仅有自己在读
			return true;

		if (hasReaders())
			// 有读取线程
			return false;

		if (writingThread == null)
			// 无写线程
			return true;

		if (!isWriter(callingThread))
			// 有写线程但不是自己
			return false;

		return true;
	}

	private int getReadAccessCount(Thread callingThread) {
		Integer accessCount = readingThreads.get(callingThread);
		if (accessCount == null)
			return 0;
		return accessCount.intValue();
	}

	private boolean hasReaders() {
		return readingThreads.size() > 0;
	}

	private boolean isReader(Thread callingThread) {
		return readingThreads.get(callingThread) != null;
	}

	private boolean isOnlyReader(Thread callingThread) {
		return readingThreads.size() == 1 && readingThreads.get(callingThread) != null;
	}

	private boolean hasWriter() {
		return writingThread != null;
	}

	private boolean isWriter(Thread callingThread) {
		return writingThread == callingThread;
	}

	private boolean hasWriteRequests() {
		return this.writeRequests > 0;
	}

}
